package com.base.monitor.module.biz.service.cfg.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.base.monitor.framework.exception.CustomerException;
import com.base.monitor.framework.result.PageResult;
import com.base.monitor.framework.util.CustomerUtils;
import com.base.monitor.model.enums.code.impl.biz.collect.CollectCode;
import com.base.monitor.model.enums.code.impl.common.ParamCode;
import com.base.monitor.model.enums.code.impl.common.ResultCode;
import com.base.monitor.model.model.biz.cfg.crierita.GatewayCriteria;
import com.base.monitor.model.model.biz.cfg.dto.GatewayGenerateCommandDto;
import com.base.monitor.model.model.biz.cfg.entity.Gateway;
import com.base.monitor.model.model.biz.cfg.vo.IndicatorCommandVo;
import com.base.monitor.module.biz.context.CfgContext;
import com.base.monitor.module.biz.context.ClientContext;
import com.base.monitor.module.biz.properties.NetworkProperties;
import com.base.monitor.module.biz.service.cfg.GatewayService;
import com.base.monitor.module.biz.util.CRC16Util;
import com.base.monitor.module.biz.util.ChannelUtils;
import com.base.monitor.module.biz.util.ConvertUtils;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@EnableConfigurationProperties(NetworkProperties.class)
public class GatewayServiceImpl implements GatewayService {

    @Value("${cfg.gatewayTimeout}")
    private String gatewayTimeout;

    @Autowired
    private NetworkProperties networkProperties;

    /**
     * 分页条件查询
     *
     * @param criteria 查询条件
     * @return
     */
    @Override
    public PageResult<Gateway> page(GatewayCriteria criteria) {
        List<Gateway> gatewayList = CfgContext.getGatewayVoList();
        if (StrUtil.isNotBlank(criteria.getGatewayIp())) {
            gatewayList = gatewayList.stream()
                    .filter(item -> item.getGatewayIp().contains(criteria.getGatewayIp()))
                    .collect(Collectors.toList());
        }

        if (StrUtil.isNotBlank(criteria.getGatewayName())) {
            gatewayList = gatewayList.stream()
                    .filter(item -> item.getGatewayName().contains(criteria.getGatewayName()))
                    .collect(Collectors.toList());
        }

        int total = gatewayList.size();
        List<Gateway> content = CustomerUtils
                .getSubListForPage(gatewayList, criteria.getPage(), criteria.getSize());
        fillOnlineTimeAndStatusAndVersion(content);
        return new PageResult<>(total, content);
    }

    /**
     * 填充onlineTime和status和version
     * @param content 待填充的数组
     */
    private void fillOnlineTimeAndStatusAndVersion(List<Gateway> content) {
        if (CollectionUtil.isEmpty(content)) {
            return;
        }
        for (Gateway vo : content) {
            Date onlineTime = CfgContext.getGatewayOnlineMap().get(vo.getGatewayId());
            if (onlineTime == null
                    || System.currentTimeMillis() - onlineTime.getTime() >= Integer.parseInt(gatewayTimeout) * 1000) {
                vo.setStatus(0);
            } else {
                vo.setStatus(1);
            }
            vo.setOnlineTime(onlineTime);

            String version = ClientContext.getVersionMap().get(vo.getCommand());
            vo.setVersion(StrUtil.isBlank(version) ? "" : version);
        }
    }

    /**
     * 下发获取版本的命令
     */
    @Override
    public void sendVersionCommand() {
        List<ChannelHandlerContext> allChannels = ClientContext.getAllChannels();
        if (CollectionUtil.isEmpty(allChannels)) {
            throw new CustomerException(CollectCode.HAS_NO_ONLINE_CLIENT);
        }
        for (ChannelHandlerContext ctx : allChannels) {
            ChannelUtils.writeToClient(networkProperties.getVersionCommand(), ctx, "获取版本");
        }
    }

    /**
     * 生成下发命令
     *
     * @param gatewayGenerateCommandDto 生成命令需要的实体信息
     * @return
     */
    @Override
    public Map<String, String> generate(GatewayGenerateCommandDto gatewayGenerateCommandDto) {

        // 1.根据网关id和serialNumber查询指标id的集合
        Gateway vo = getGatewayListVoFromList(CfgContext.getGatewayVoList(), gatewayGenerateCommandDto.getMessage());
        if (vo == null || CollectionUtil.isEmpty(vo.getIndicators())) {
            return null;
        }

        // 2.根据指标id的集合查询指标的信息
        List<IndicatorCommandVo> indicators = vo.getIndicators();
        if (CollectionUtil.isEmpty(indicators)) {
            return null;
        }

        // AA  FF  00  01  01  01 04  02  01  03  00  25  00  01  95  C1 12 34 BB DD
        //        // AA全部开头
        //        // FF每条开始标识位
        //        // 00 01 指标id
        //        // 01 串口号
        //        // 01 采集频率
        //        // 04 返回值的位置
        //        // 02 返回多少位
        //        // 01 03 00 25 00 01 命令
        //        // 95 C1 CRC校验
        //        // 12 34 BB CC 每条结束结束符
        //        // 12 34 BB DD 全部结束结束符
        // 3.根据指标生成命令
        StringBuilder sb = new StringBuilder();
        sb.append("AA");
        for (int i = 0; i < indicators.size(); i++) {

            IndicatorCommandVo indicator = indicators.get(i);

            // 准备数据
            String indicatorHex = ConvertUtils.decToHex(Integer.parseInt(indicator.getId()), 4);
            String serialNumberHex = ConvertUtils.decToHex(indicator.getSerialNumber(), 2);
            String frequencyHex = ConvertUtils.decToHex(indicator.getFrequency(), 2);
            String startBitHex = ConvertUtils.decToHex(indicator.getStartBit(), 2);
            String[] takeBitArray = indicator.getTakeBit().split(",");
            int count = 0;
            for (String num : takeBitArray) {
                count += Integer.parseInt(num);
            }
            String takeBitHex = ConvertUtils.decToHex(count, 2);
            String command = indicator.getCommand();
            String checkHex = "";
            if (indicator.getAuthCode().toLowerCase().equals("crc")) {
                checkHex = CRC16Util.crc16(command);
            } else if (indicator.getAuthCode().toLowerCase().equals("cs")) {
                checkHex = CRC16Util.cs(command);
            }

            // 添加数据
            sb.append("FF").append(indicatorHex).append(serialNumberHex).append(frequencyHex).append(startBitHex)
                    .append(takeBitHex).append(command).append(checkHex);

            if (i == indicators.size() - 1) {
                sb.append("1234BBDD");
            } else {
                sb.append("1234BBCC");
            }
        }

        // 4.返回数据
        Map<String, String> map = new HashMap<>();
        map.put("command", sb.toString());
        return map;
    }

    /**
     * 根据网关id从gatewayList中获取对应的记录
     * @param gatewayList 待获取记录的集合
     * @param message 网关的连接消息
     * @return
     */
    private Gateway getGatewayListVoFromList(List<Gateway> gatewayList, String message) {
        if (CollectionUtil.isEmpty(gatewayList)) {
            return null;
        }
        for (Gateway vo : gatewayList) {
            if (vo.getCommand().equals(message)) {
                return vo;
            }
        }
        return null;
    }

    /**
     * 下发指令
     *
     * @param gatewayGenerateCommandDto 下发指令需要的实体信息
     */
    @Override
    public void writeCollectCommand(GatewayGenerateCommandDto gatewayGenerateCommandDto) {
        // 1.校验参数
        String command = gatewayGenerateCommandDto.getCommand();
        if (StrUtil.isBlank(command)) {
            throw new CustomerException(ParamCode.PARAM_IS_BLANK);
        }

        // 2.获取信道
        ClientContext.ChannelInfo channelInfo = ClientContext
                .getChannelInfoByCommand(gatewayGenerateCommandDto.getMessage());
        if (channelInfo == null || channelInfo.getCtx() == null) {
            throw new CustomerException(ResultCode.HANDLE_DATA_NOT_EXISTED, "连接命令对应的信道不存在或已离线，无法下发命令");
        }

        // 3.发送指令
        ChannelUtils.writeToClient(command, channelInfo.getCtx(), "下发定时采集指令");

    }

    /**
     * 查看下发结果
     *
     * @param gatewayGenerateCommandDto 查看结果需要的实体信息
     */
    @Override
    public void checkCollectResult(GatewayGenerateCommandDto gatewayGenerateCommandDto) {
        ChannelHandlerContext ctx = ClientContext
                .getChannelHandlerContextByCommand(gatewayGenerateCommandDto.getMessage());
        if (ctx != null
                && ClientContext.getSendCollectMap().containsKey(gatewayGenerateCommandDto.getMessage())
                && StrUtil.isNotBlank(ClientContext.getSendCollectMap().get(gatewayGenerateCommandDto.getMessage()))) {
            return;
        }
        throw new CustomerException(CollectCode.SEND_COLLECT_COMMAND_FAIL);
    }
}
